﻿using System.Diagnostics;
using System.IO;
using System.IO.Packaging;
using System.Reflection;

namespace System.AutoUpdate.Bootstrap
{
    /// <summary>
    /// Represents bootstrap application that is compiled at runtime during update.
    /// Bootstrap opens .aup package file and performs the update to target directory.
    /// 
    /// Example usages:
    /// UpdateBootstrap.exe "C:\Temp" MyApplication.exe
    ///  - performs .aup updates in temp to current directory
    /// UpdateBootstrap.exe "C:\Temp" MyApplication.exe "C:\Program Files\MyApp\"
    ///  - performs .aup updates in temp to selected directory
    /// </summary>
    public class UpdateBootstrap
    {
        [STAThread]
        public static void Main(string[] args)
        {
            //
            // Parameter check
            //

            if (args.Length < 2 || args.Length > 3)
            {
                return;
            }

            //
            // Accepting two parameters
            //

            string tempDir = args[0];
            string targetExecutable = args[1];
            string targetDir = args.Length == 3 ? args[2] : Assembly.GetExecutingAssembly ().Location;

            //
            // Checks
            //

            if (!Directory.Exists (tempDir) || !Directory.Exists (targetDir) || File.Exists (targetDir + targetExecutable))
            {
                return;
            }

            //
            // Wait for updating process to exit
            //

            Process updateProcess = GetProcessByFilename (targetExecutable);

            if (updateProcess != null)
            {
                updateProcess.WaitForExit ();
            }

            //
            // Find aup file in temp dir
            //

            string[] files = Directory.GetFiles (tempDir);

            foreach (string file in files)
            {
                //
                // Perform update form .aup packages
                //

                if (Path.GetExtension (tempDir + file) != ".aup")
                {
                    continue;
                }

                using (FileStream fileStream = new FileStream(tempDir + file, FileMode.Open))
                {
                    UnzipFilesFromStream(fileStream, targetDir);
                }
            }
        }

        /// <summary>
        /// Returns Process object by filename
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        private static Process GetProcessByFilename (string filename)
        {
            Process[] processes = Process.GetProcesses ();

            foreach (Process process in processes)
            {
                if (Path.GetFileName (process.MainModule.FileName) == filename)
                {
                    return process;
                }
            }

            return null;
        }

        /// <summary>
        /// Function unzips files in zip stream to base folder
        /// </summary>
        /// <param name="source"></param>
        /// <param name="baseFolder"></param>
        private static void UnzipFilesFromStream(Stream source, string baseFolder)
        {
            using (Package package = Package.Open(source, FileMode.Open))
            {
                foreach (PackagePart zipPart in package.GetParts())
                {
                    string path = Path.Combine(baseFolder, zipPart.Uri.ToString().Substring(1));

                    using (Stream zipStream = zipPart.GetStream())
                    {
                        using (FileStream fileStream = new FileStream(path, FileMode.Create))
                        {
                            zipStream.CopyTo(fileStream);
                        }
                    }
                }
            }
        }
    }
}
